package com.ruoyi.common.utils.weCat;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.AuthUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.json.JsonUtils;
import com.ruoyi.common.utils.map.MapLocalUtils;
import com.ruoyi.common.utils.rest.RestTemplateUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.annotation.PostConstruct;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author luobo
 * @Date 2021/8/9 11:03
 * @Describetion 微信小程序登录、支付处理工具类
 */
//https://blog.csdn.net/qi923701/article/details/81534820
@Component
public class WeCatUtils {
    private static final Logger log = LoggerFactory.getLogger(WeCatUtils.class);

    @Autowired
    RestTemplateUtil restTemplateUtil;
    @Autowired
    RedisCache redisCache;
    @Autowired
    private Environment env;
    private static String appId;
    private static String mchId;
    private static String appSecret;
    private static String paySignKey;
    private static String spbillCreateIp;
    private static String accessToken;
    private static String pubMessageUrl;

    @PostConstruct
    public void init(){
        appId = env.getProperty("wx.appid");
        mchId = env.getProperty("wx.mchId");
        appSecret = env.getProperty("wx.appSecret");
        paySignKey = env.getProperty("wx.paySignKey");
        spbillCreateIp = env.getProperty("wx.spbillCreateIp");
        pubMessageUrl = env.getProperty("wx.pubMessageUrl");
        accessToken = env.getProperty("user.accessToken");
    }

    /**
     * 小程序登录，根据前端传来的code获取openId和sessionKey
     * @param code
     * @return
     * @throws Exception
     */
    public JSONObject queryWxOpenId(String code) throws Exception {
        String url = env.getProperty("wx.jsCodeUrl") + "?appid=" + env.getProperty("wx.appid") + "&secret=" + env.getProperty("wx.appSecret") + "&js_code=" + code + "&grant_type=authorization_code";
        JSONObject jsonObject = AuthUtil.doGetJson(url);
        return jsonObject;
    }
    public JSONObject queryWxOpenIdForH5(String code) throws Exception {
        return queryWxOpenIdForH5(appId, appSecret, code);
    }
    public JSONObject queryWxOpenIdForH5(String appId, String appSecret, String code) throws Exception {
        String url = env.getProperty("wx.jsCodeUrlByH5");
        ResponseEntity<String> responseEntity = restTemplateUtil.get(url, String.class, appId, appSecret, code);
        if (responseEntity.getStatusCode().value() == HttpStatus.OK.value()) {
            String body = responseEntity.getBody();
            JSONObject object = JSON.parseObject(body);
            return object;
        } else {
            throw new RuntimeException("请求获取微信access_token异常");
        }
    }

    /**
     * 获取请求token
     * @return
     */
    public JSONObject queryWxAccessToken() throws Exception{
        return queryWxAccessToken(appId, appSecret);
    }
    public JSONObject queryWxAccessToken(String appId, String appSecret) throws Exception{
        String tokenUrl = env.getProperty("wx.tokenUrl");
        ResponseEntity<String> responseEntity = restTemplateUtil.get(tokenUrl, String.class, appId, appSecret);
        if (responseEntity.getStatusCode().value() == HttpStatus.OK.value()) {
            String body = responseEntity.getBody();
            JSONObject object = JSON.parseObject(body);
            String access_Token = object.getString("access_token");
            String expires_in = object.getString("expires_in");
            System.out.println("有效时长expires_in：" + expires_in);
            return object;
        } else {
            throw new RuntimeException("请求获取微信access_token异常");
        }
    }

    /**
     * 获取微信支付tiket
     * @param accessToken 请求token
     * @param type 请求类型
     * @return
     * @throws Exception
     */
    public JSONObject queryWxJsapiTicket(String accessToken, String type) throws Exception{
        String tokenUrl = env.getProperty("wx.jsapiTicket");
        ResponseEntity<String> responseEntity = restTemplateUtil.get(tokenUrl, String.class, accessToken, type);
        if (responseEntity.getStatusCode().value() == HttpStatus.OK.value()) {
            String body = responseEntity.getBody();
            JSONObject object = JSON.parseObject(body);
            String Access_Token = object.getString("access_token");
            String expires_in = object.getString("expires_in");
            System.out.println("有效时长expires_in：" + expires_in);
            return object;
        } else {
            throw new RuntimeException("获取微信jsapi_ticket异常");
        }
    }

    /**
     * 解密用户敏感数据获取用户信息
     * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据
     * @param sessionKey    数据进行加密签名的密钥
     * @param iv            加密算法的初始向量
     * @return
     * */
    //https://www.cnblogs.com/tomingto/p/12133781.html
    public JSONObject getDecryptedData(String encryptedData, String sessionKey, String iv) throws Exception {
        // 被加密的数据
        byte[] dataByte = Base64Utils.decode(encryptedData.getBytes());
        // 加密秘钥
        byte[] keyByte = Base64Utils.decode(sessionKey.getBytes());
        // 偏移量
        byte[] ivByte = Base64Utils.decode(iv.getBytes());
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "utf-8");
                //System.out.println("------1-----"+result);
                return JSON.parseObject(result);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 微信小程序支付
     * @param openId 被通知人openId
     * @param body 请求详情描述，如付款给 XXX
     * @param notifyUrl 回调地址
     * @param amount 转账金额
     * @return
     */
    //https://blog.csdn.net/u011134780/article/details/90609548
    public Map<String, Object> payment(String openId, String orderId, String body, String notifyUrl, BigDecimal amount) throws Exception {
        //测试用1分钱
//        amount = new BigDecimal(0.01);
        if(null == amount || amount.compareTo(BigDecimal.ZERO) < 1){
            throw new Exception("调用支付接口参数错误，支付金额必须大于0");
        }
        BigDecimal payAmount = amount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_DOWN);//订单金额；分;
        String nonce_str = PayUtil.getRandomStringByLength(32).toUpperCase(); //生成的随机字符串
        String trade_type = "JSAPI";
        //组装参数，用户生成统一下单接口的签名
        Map<String, String> packageParams = new HashMap<>();
        packageParams.put("appid", appId);
        packageParams.put("mch_id", mchId);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("body", body); //订单详情,如付款给 XXX
        packageParams.put("out_trade_no", orderId);//商户订单号,自己的订单ID
        packageParams.put("total_fee", String.valueOf(payAmount));//支付金额，这边需要转成字符串类型，否则后面的签名会失败
        packageParams.put("spbill_create_ip", spbillCreateIp); //求客户端ip
        packageParams.put("notify_url", notifyUrl);//支付成功后的回调地址
        packageParams.put("trade_type", trade_type);//支付方式//交易类型，小程序支付的固定值为JSAPI
        packageParams.put("openid", openId);//用户的openID，自己获取
        String prestr = PayUtil.createLinkString(packageParams); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String mysign = PayUtil.sign(prestr, paySignKey, "utf-8").toUpperCase(); //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
        String xml = "<xml>"
                + "<appid>" + appId + "</appid>" //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
                + "<body><![CDATA[" + body + "]]></body>"
                + "<mch_id>" + mchId + "</mch_id>"
                + "<nonce_str>" + nonce_str + "</nonce_str>"
                + "<notify_url>" + notifyUrl + "</notify_url>"
                + "<openid>" + openId + "</openid>"
                + "<out_trade_no>" + orderId + "</out_trade_no>"
                + "<spbill_create_ip>" + spbillCreateIp + "</spbill_create_ip>"
                + "<total_fee>" + payAmount + "</total_fee>"//支付的金额，单位：分
                + "<trade_type>" + trade_type + "</trade_type>"
                + "<sign>" + mysign + "</sign>"
                + "</xml>";
        log.info(xml);
        //调用统一下单接口，并接受返回的结果
        String result = HttpsClientUtils.httpPost(env.getProperty("wx.payUrl"), xml);
        Map<String, Object> resultMap = new HashMap<>();//返回给小程序端需要的参数
        try {
            //解析支付平台返回的结果并存储在map中
            Map map = PayUtil.doXMLParse(result);
            String result_code = (String) map.get("result_code");//返回状态码
            if("SUCCESS".equals(result_code)) {
                String prepay_id = (String) map.get("prepay_id");//返回的预付单信息
                resultMap.put("result_code", result_code);
                resultMap.put("nonceStr", nonce_str);
                resultMap.put("package", "prepay_id=" + prepay_id);
                Long timeStamp = System.currentTimeMillis() / 1000;
                //这边要将返回的时间戳转化成字符串，不然小程序端调用wx.requestPayment方法会报签名错误
                resultMap.put("timeStamp", timeStamp + "");
                //拼接签名需要的参数
                String stringSignTemp = "appId=" + appId + "&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
                //再次签名，这个签名用于小程序端调用wx.requesetPayment方法
                String paySign = PayUtil.sign(stringSignTemp, paySignKey, "utf-8").toUpperCase(); //支付调用
                resultMap.put("paySign", paySign);
                resultMap.put("signType", "MD5");
                resultMap.put("appid", appId);
                resultMap.put("payStatus", "1");
                log.info("----------------------------------------------------我支付了"+ resultMap);
            }else{
                resultMap.put("payStatus", "2");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("解析微信返回结果出现异常");
            resultMap.put("payStatus", "2");
        }
        return resultMap;
    }

    /**
     * 微信支付订单状态查询
     * @param map 微信回调返回的完整参数
     * @return
     * @throws Exception
     */
    //https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
    /*public Map<String, Object> queryOrderStatus(Map map) throws Exception {
        String nonce_str = PayUtil.getRandomStringByLength(32).toUpperCase(); //生成的随机字符串
        //组装参数，用户生成统一下单接口的签名
        Map<String, String> packageParams = new HashMap<>();
        packageParams.put("appid", appId);
        packageParams.put("mch_id", mchId);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("transaction_id", (String) map.get("transaction_id"));//商户订单号,自己的订单ID
        String prestr = PayUtil.createLinkString(packageParams); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String mysign = PayUtil.sign(prestr, paySignKey, "utf-8").toUpperCase(); //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
        String xml = "<xml>"
                + "<appid>" + appId + "</appid>" //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
                + "<mch_id>" + mchId + "</mch_id>"
                + "<nonce_str>" + nonce_str + "</nonce_str>"
                + "<transaction_id>" + (String) map.get("transaction_id") + "</transaction_id>"
                + "<sign>" + mysign + "</sign>"
                + "</xml>";
        log.info(xml);
        //微信订单状态查询，并接受返回的结果
        String result = HttpsClientUtils.httpPost(env.getProperty("wx.orderQuery"), xml);
        log.info("微信订单状态查询返回结果：" + result);
        Map<String, Object> resultMap = new HashMap<>();//返回给小程序端需要的参数
        try {
            //解析支付平台返回的结果并存储在map中
            resultMap = PayUtil.doXMLParse(result);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("解析微信返回结果出现异常");
        }
        return resultMap;
    }*/

    public Map<String, Object> queryOrderStatus(Map map) throws Exception {
        String nonce_str = PayUtil.getRandomStringByLength(32).toUpperCase(); //生成的随机字符串
        //组装参数，用户生成统一下单接口的签名
        Map<String, String> packageParams = new HashMap<>();
        packageParams.put("appid", appId);
        packageParams.put("mch_id", mchId);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("transaction_id", (String) map.get("transaction_id"));//商户订单号,自己的订单ID
        String prestr = PayUtil.createLinkString(packageParams); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String mysign = PayUtil.sign(prestr, paySignKey, "utf-8").toUpperCase(); //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
        String xml = "<xml>"
                + "<appid>" + appId + "</appid>" //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
                + "<mch_id>" + mchId + "</mch_id>"
                + "<nonce_str>" + nonce_str + "</nonce_str>"
                + "<transaction_id>" + (String) map.get("transaction_id") + "</transaction_id>"
                + "<sign>" + mysign + "</sign>"
                + "</xml>";
        log.info(xml);
        //微信订单状态查询，并接受返回的结果
        ResponseEntity<String> temp = restTemplateUtil.post(env.getProperty("wx.orderQuery"), xml, String.class);
        Map<String, Object> resultMap = new HashMap<>();//返回给小程序端需要的参数
        if (temp.getStatusCode().value() == HttpStatus.OK.value()) {
            String result = temp.getBody();
            //System.out.println("------------------" + result);
            log.info("微信订单状态查询返回结果：" + result);
            resultMap = PayUtil.doXMLParse(result);
        } else {
            throw new RuntimeException("解析微信返回结果出现异常");
        }
        return resultMap;
    }
/*<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<mch_id><![CDATA[1615046892]]></mch_id>
<appid><![CDATA[wxc22b09c32b362208]]></appid>
<openid><![CDATA[osOs-45TZHkMc_Oet7gM6kshJ-yo]]></openid>
<is_subscribe><![CDATA[N]]></is_subscribe>
<trade_type><![CDATA[JSAPI]]></trade_type>
<trade_state><![CDATA[SUCCESS]]></trade_state>
<bank_type><![CDATA[OTHERS]]></bank_type>
<total_fee>1</total_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<cash_fee>1</cash_fee>
<cash_fee_type><![CDATA[CNY]]></cash_fee_type>
<transaction_id><![CDATA[4200001145202110217867250797]]></transaction_id>
<out_trade_no><![CDATA[20211021101930091258]]></out_trade_no>
<attach><![CDATA[]]></attach>
<time_end><![CDATA[20211021102011]]></time_end>
<trade_state_desc><![CDATA[支付成功]]></trade_state_desc>
<nonce_str><![CDATA[FupheAkaOyKT2lmX]]></nonce_str>
<sign><![CDATA[01E99845E8B77068A33DED0057549B84]]></sign>
</xml>*/

    /**
     * 生成小程序二维码
     * @param userInviteId 邀请人id
     * @return
     */
    public String createAppletsQrCode(String userInviteId) {
        PrintWriter printWriter = null;
        OutputStream os = null;
        try {
            File file = new File(RuoYiConfig.getAppletsQrCodePath() + File.separator + userInviteId + ".png");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            if (!file.exists()) {
                file.createNewFile();
            }
            JSONObject jo = queryWxAccessToken();
            String wxCodeURL = env.getProperty("wx.appletsQrCode") + "?access_token=" + jo.get("access_token");
            URL url = new URL(wxCodeURL);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("POST");// 提交模式
            // 发送POST请求必须设置如下两行
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            printWriter = new PrintWriter(httpURLConnection.getOutputStream());
            // 发送请求参数
            JSONObject paramJson = new JSONObject();
            paramJson.put("scene", userInviteId);
            //paramJson.put("scene", "userInviteId=" + invitationCode);
//          paramJson.put("page", "pages/index/index"); //小程序暂未发布我没有带page参数
            printWriter.write(paramJson.toString());
            // flush输出流的缓冲
            printWriter.flush();
            //开始获取数据
            BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream());
            os = new FileOutputStream(file);
            int len;
            byte[] arr = new byte[1024];
            while ((len = bis.read(arr)) != -1) {
                os.write(arr, 0, len);
                os.flush();
            }
            return file.getAbsolutePath();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != printWriter) {
                printWriter.close();
            }
            try {
                if (null != os) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
    * 企业付款(提现)
    * @param openid 商户appid下，某用户的openid
    * @throws Exception
    */
    public Map<String, Object> transfers(String openid, String userName, BigDecimal amount) throws Exception {
        //测试用1分钱
        amount = new BigDecimal(1);
        if(null == amount || amount.compareTo(BigDecimal.ZERO) < 1){
         throw new Exception("调用支付接口参数错误，支付金额必须大于0");
        }
        String nonce_str = PayUtil.getRandomStringByLength(32).toUpperCase(); //生成的随机字符串
        String trade_type = "JSAPI";
        HashMap<String, String> txMap = new HashMap<String, String>();
        BigDecimal payAmount = amount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_DOWN);//订单金额；分;
        txMap.put("mch_appid",appId);// 商户账号appid
        txMap.put("mchid", mchId);//mchid
        txMap.put("nonce_str", nonce_str);//随机字符串
        txMap.put("partner_trade_no", "123456789");//商户订单号
        txMap.put("openid", openid);// 用户openid
        txMap.put("check_name", "NO_CHECK");// 校验用户姓名选项
        txMap.put("amount", String.valueOf(payAmount));// 金额
        txMap.put("desc", "余额提现");// 企业付款描述信息
        txMap.put("spbill_create_ip", spbillCreateIp);// Ip地址
        String prestr = PayUtil.createLinkString(txMap); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String mysign = PayUtil.sign(prestr, paySignKey, "utf-8").toUpperCase(); //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
        String xml = "<xml>"
             + "<mch_appid>" + appId + "</mch_appid>" //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
             + "<mchid>" + mchId + "</mchid>"
             + "<nonce_str>" + nonce_str + "</nonce_str>"
             + "<partner_trade_no>" + "123456789" + "</partner_trade_no>"
             + "<openid>" + openid + "</openid>"
             + "<check_name>" + "NO_CHECK" + "</check_name>"
             + "<amount>" + payAmount + "</amount>"//支付的金额，单位：分
             + "<desc>" + "余额提现" + "</desc>"
             + "<spbill_create_ip>" + spbillCreateIp + "</spbill_create_ip>"
             + "<sign>" + mysign + "</sign>"
             + "</xml>";
        log.info(xml);
        System.out.println(xml);
        String result = HttpsClientUtils.httpPost(env.getProperty("wx.wxTransfers"), xml,mchId,"/WithCert/apiclient_cert.p12");
        Map<String, Object> resultMap = new HashMap<>();//返回给后端参数
        try {
            //解析支付平台返回的结果并存储在map中
            Map map = PayUtil.doXMLParse(result);
            String result_code = (String) map.get("result_code");//返回状态码
            if("SUCCESS".equals(result_code)) {
                String prepay_id = (String) map.get("prepay_id");//返回的预付单信息
                resultMap.put("result_code", result_code);
                resultMap.put("nonceStr", nonce_str);
                resultMap.put("package", "prepay_id=" + prepay_id);
                Long timeStamp = System.currentTimeMillis() / 1000;
                //这边要将返回的时间戳转化成字符串，不然小程序端调用wx.requestPayment方法会报签名错误
                resultMap.put("timeStamp", timeStamp + "");
                //拼接签名需要的参数
                String stringSignTemp = "appId=" + appId + "&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
                //再次签名，这个签名用于小程序端调用wx.requesetPayment方法
                String paySign = PayUtil.sign(stringSignTemp, paySignKey, "utf-8").toUpperCase(); //支付调用
                resultMap.put("paySign", paySign);
                resultMap.put("signType", "MD5");
                resultMap.put("appid", appId);
                resultMap.put("payStatus", "1");
            }else{
                resultMap.put("payStatus", "2");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("解析微信返回结果出现异常");
            resultMap.put("payStatus", "2");
        }
        return resultMap;
    }

     //*微信授权空白页域名*/
    public String getAuthorizationUrl(String authorRedirectUri) throws Exception {
        log.info("authorRedirectUri----------------" + authorRedirectUri);
        return MessageFormat.format(env.getProperty("wx.authorUrl"), appId, authorRedirectUri);
    }

    public static void main(String[] args) {
        String appId = "ddddd";
        String authorRedirectUri = "ffffffffffff";
        String ss = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&redirect_uri={1}&response_type=code&scope=snsapi_base&state=Authorization#wechat_redirect";
        String dd = MessageFormat.format(ss, appId, authorRedirectUri);
        System.out.println("-----------" + dd);
    }

    /**
     * 微信公众号签名授权
     * @param url
     * @return
     * @throws Exception
     */
    public Map<String, String> getPermissionsToSign(String url) throws Exception {
        String ticket = redisCache.getCacheObject(env.getProperty("user.jsapiTicket"));
        if(StringUtils.isEmpty(ticket)){
            String accessToken = redisCache.getCacheObject(env.getProperty("user.accessToken"));
            if(StringUtils.isEmpty(accessToken)){
                JSONObject object = queryWxAccessToken();
                accessToken = object.get("access_token").toString();
                redisCache.setCacheObject(env.getProperty("user.accessToken"), accessToken, Integer.valueOf(object.get("expires_in").toString()), TimeUnit.SECONDS);
            }
            JSONObject object = queryWxJsapiTicket(accessToken, "jsapi");
            ticket = object.get("ticket").toString();
            redisCache.setCacheObject(env.getProperty("user.jsapiTicket"), ticket, Integer.valueOf(object.get("expires_in").toString()), TimeUnit.SECONDS);
        }
        HashMap<String, String> returnMap = new HashMap<>();
        returnMap.put("noncestr", PayUtil.getRandomStringByLength(32).toUpperCase());
        returnMap.put("jsapi_ticket", ticket);
        returnMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        returnMap.put("url", url);
        String ksort = MapLocalUtils.createSortString(returnMap);
        String sign = PayUtil.shaEncode(ksort);
        returnMap.put("sign", sign); //签名
        returnMap.put("appid",appId);
        return returnMap;
    }

    /**
     * 公众号消息发送
     */
    //https://developers.weixin.qq.com/doc/offiaccount/Subscription_Messages/api.html
    public void sendTemplate(Object template) throws Exception {
        sendTemplate(appId, appSecret, template);
    }

    /**
     * 公众号消息发送
     */
    //https://developers.weixin.qq.com/doc/offiaccount/Subscription_Messages/api.html
    public void sendTemplate(String appId, String appSecret, Object template) throws Exception {
        //String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend?access_token=ACCESS_TOKEN";
        String accessToken = redisCache.getCacheObject(env.getProperty("user.accessToken"));
        if(StringUtils.isEmpty(accessToken)){
            JSONObject object = queryWxAccessToken(appId, appSecret);
            accessToken = object.get("access_token").toString();
            redisCache.setCacheObject(accessToken, accessToken, Integer.valueOf(object.get("expires_in").toString()), TimeUnit.SECONDS);
        }
        String url = MessageFormat.format(pubMessageUrl, accessToken);
        System.out.println("---222-"+JsonUtils.pojoToJson(template));
        ResponseEntity<String> temp = restTemplateUtil.post(url, JsonUtils.pojoToJson(template), String.class);
        System.out.println("------" + temp);
    }
}